For the remainder of this tutorial, when you see <project>, replace it with Ejb3Tutorial2.
Create the Project
Pull down the File menu and select new:project
Select Java Project
Click Next
Enter <project>
Under Project Layout select create separate source and output folders
Click Finish
Select <project>, right-click and select new:Source Folder
Enter conf for the name
Click on Finish
Select <project>, right-click and select new:Source Folder
Enter test for the name
Click on Finish
Edit your project properties
Now that we have created a user library, we can add that user library to our project:
Select <project>, and press alt-enter or right-click and select properties.
Select Java Build Path
Select the Libraries tab
Click on Add Library
Select User Library and click Next
Click on the box next to EJB3_EMBEDDABLE and click Finish
Click Add Library
Select JUnit and click Next
In the pull-down list, select JUnit 4 and click Finish
Click on OK to make the change to your project's classpath
Setup the configuration files
The JBoss Embeddable container looks for several files in the classpath. To keep all of these in one place, we'll add another source directory to our project and then import several files into that directory.
Select the conf folder under <project>
Pull down the File menu and select Import
Expand General
Select File System and click on Next
Click on Browse and go to the following directory: C:/libs/jboss-EJB-3.0_Embeddable_ALPHA_9/conf
Click on OK
You'll see conf in the left pane, select it
Verify that the Into folder: lists <project>/conf (if not, just enter it or browse to it)
Click Finish
Expand the conf directory and verify that the files are now there
Add Resource Adapter Archive(RAR)
The Java Connector system defines Resource Adapter Archive files (RAR files). We need to add a few RAR files into the class path. We will import two more files into the conf directory:
Select the conf folder
Pull down the File menu and select Import
Expand General
Select File System and click on Next
Click on Browse and go to the following directory: C:/libs/jboss-EJB-3.0_Embeddable_ALPHA_9/lib
Select jcainflow.rar and jms-ra.rar
Click Finish
Create a jndi.properties file
Note, depending on the version of the embeddable container you download, you might already have a file called jndi.properties. If you do, skip to the next section.
Select the conf directory, right-click and select new then select File
Enter the name jndi.properties and click finish
Enter the following 2 lines then save and close the file:
This example presents a utility class we'll be using later. The container needs a persistence.xml file to operate. This file must be found under a META-INF directory somewhere in the classpath or the embeddable container will not start. The file's name is persistence.xml with a lower-case 'p'. On a Unix system, this will make a difference. On a PC, this won't make a difference and it is one of those things that might work on your machine but not on the linux build box.
Select your src directory
Right-click, select New:Folder
Enter META-INF
Click OK
Select META-INF
Right-lick, select New:File
Enter persistence.xml
Click Finish
Copy the following example into your new file then save it by pressing ctrl-s
persistence.xml
<?xmlversion="1.0"encoding="UTF-8"?><persistence><persistence-unitname="custdb"><!-- This persistence unit uses the default data source that JBoss --><!-- defines called DefaultDS. If we wanted to use our own data --><!-- source, we'd need to define a custom data source somewhere. --><!-- That somewhere is vendor specific. --><!-- In the case of JBoss, since we're using the embedded container, --><!-- we need to add two beans in a file called --><!-- embedded-jboss-beans.xml. We name the first --><!-- HypersonicLocalServerDSBootstrap and we name the second --><!-- HypersonicLocalServerDS. This two step process defines a data --><!-- source. --><!-- In the first bean definition, we additionally bind it in Jndi --><!-- under some name. If we used the name --><!-- java:/HypersonicLocalServerDS then we would use the following --><!-- entry to use that data source instead of the default one: --><!-- <jta-data-source>java:/HypersonicLocalServerDS</jta-data-source> --><jta-data-source>java:/DefaultDS</jta-data-source><properties><propertyname="hibernate.hbm2ddl.auto"value="create-drop"/></properties></persistence-unit></persistence>
The Entity Model
Creating the Entity Model
We need to create our entity model. There are three classes in it:
Address
Company
Person
To create the basic classes:
Select the src folder under <project>
Right-click and select New::Class
For the package, enter entity
For the name, enter Address, Company, and Person respectively.
Type or enter the source provided below for each of the three classes.
Changes
The entity model is changed slightly from the first JPA tutorial. There is one change in Company.java. Review that class' comments to understand that change. The quick summary is that we've added eager fetching to a relationship.
Address.java
packageentity;importjavax.persistence.Embeddable;/**
* There are no changes to this entity. Its embedded in the Person and Company
* and as such, it is automatically fetched.
*/
@Embeddable
publicclass Address {privateString streetAddress1;privateString streetAddress2;privateString city;privateString state;privateString zip;public Address(){}public Address(finalString sa1, finalString sa2, finalString city,
finalString state, finalString zip){
setStreetAddress1(sa1);
setStreetAddress2(sa2);
setCity(city);
setState(state);
setZip(zip);}publicString getCity(){return city;}publicvoid setCity(finalString city){this.city = city;}publicString getState(){return state;}publicvoid setState(finalString state){this.state = state;}publicString getStreetAddress1(){return streetAddress1;}publicvoid setStreetAddress1(finalString streetAddress1){this.streetAddress1 = streetAddress1;}publicString getStreetAddress2(){return streetAddress2;}publicvoid setStreetAddress2(finalString streetAddress2){this.streetAddress2 = streetAddress2;}publicString getZip(){return zip;}publicvoid setZip(finalString zip){this.zip = zip;}}
Company.java
packageentity;importjava.util.ArrayList;importjava.util.Collection;importjava.util.List;importjavax.persistence.CascadeType;importjavax.persistence.Embedded;importjavax.persistence.Entity;importjavax.persistence.FetchType;importjavax.persistence.GeneratedValue;importjavax.persistence.GenerationType;importjavax.persistence.Id;importjavax.persistence.OneToMany;/**
* There is one significant change to this Entity from the one we used in the
* first JPA tutorial. Review the employees attribute for details on what and
* why.
*/
@Entitypublicclass Company {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)privateLong id;privateString name;
@Embedded
private Address address;/**
* This attribute has one important (and significant change) from the
* original Company.java. We've added eager fetching to this relationship.
* By default, one to many relationships are lazily loaded. This means the
* collection will not actually be brought back from the database unless it
* is specifically used. So long as the Company instance is managed, this is
* not a problem. Once the object is no longer managed, however, lazily
* loaded references that have not been touched are not available (according
* to the specification, what happens is undefined).
*
* In the previous example, we did everything in a single transaction. But
* now we have a transaction starting and stopping on each method in the
* session bean, so as soon as we return from one of the session bean
* methods, the transaction is closed. Once the transaction is closed, all
* managed objects are flushed from the entity manager (and therefore no
* longer managed).
*/
@OneToMany(mappedBy = "job", cascade = { CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.REFRESH}, fetch = FetchType.EAGER)privateCollection<Person> employees = newArrayList<Person>();public Company(){}public Company(finalString name, final Address address,
final Person... persons){
setName(name);
setAddress(address);for(Person p : persons){
getEmployees().add(p);}}public Address getAddress(){return address;}publicvoid setAddress(final Address address){this.address = address;}publicCollection<Person> getEmployees(){return employees;}publicvoid setEmployees(finalList<Person> newStaff){// fire everybodyfinalList<Person> clone = newArrayList<Person>(employees);for(Person p : clone){
fire(p);}for(Person p : newStaff){
hire(p);}}publicLong getId(){return id;}publicvoid setId(finalLong id){this.id = id;}publicString getName(){return name;}publicvoid setName(finalString name){this.name = name;}publicvoid hire(final Person p){
employees.add(p);
p.setJob(this);}publicvoid fire(final Person p){
employees.remove(p);
p.setJob(null);}
@Overridepublicboolean equals(finalObject rhs){if(rhs instanceof Company){return((Company) rhs).getId() == getId();}returnfalse;}
@Overridepublicint hashCode(){return101* getId().hashCode();}}
Person.java
packageentity;importjavax.persistence.Embedded;importjavax.persistence.Entity;importjavax.persistence.GeneratedValue;importjavax.persistence.Id;importjavax.persistence.ManyToOne;/**
* Give the tests and assertions so far, there's no need to fix this problem.
*/
@Entitypublicclass Person {
@Id
@GeneratedValue
int id;privateString firstName;privatechar middleInitial;privateString lastName;
@Embedded
private Address address;
@ManyToOne
private Company job;public Person(){}public Person(finalString fn, finalchar mi, finalString ln,
final Address address){
setFirstName(fn);
setMiddleInitial(mi);
setLastName(ln);
setAddress(address);}publicString getFirstName(){return firstName;}publicvoid setFirstName(finalString firstName){this.firstName = firstName;}publicint getId(){return id;}publicvoid setId(finalint id){this.id = id;}publicString getLastName(){return lastName;}publicvoid setLastName(finalString lastName){this.lastName = lastName;}publicchar getMiddleInitial(){return middleInitial;}publicvoid setMiddleInitial(finalchar middleInitial){this.middleInitial = middleInitial;}public Address getAddress(){return address;}publicvoid setAddress(final Address address){this.address = address;}public Company getJob(){return job;}publicvoid setJob(Company job){this.job = job;}
@Overridepublicboolean equals(finalObject rhs){if(rhs instanceof Person){return((Person) rhs).getId() == getId();}returnfalse;}
@Overridepublicint hashCode(){return101* getId();}}
The Dao and its Interface
We will create a session bean that wraps access to the company behind a session bean interface. This is another example of the Data Access Object (dao) pattern. This is one of the typical ways we use a session bean.
This dao looks pretty much the same as the session bean from our first EJB example. As with the first example, we've placed the interface in one package and the implementation (the session bean) in a second interface.
CompanyDao.java
packagedao;importentity.Company;/**
* I do not define whether I am remote or local so we won't know how to lookup
* implementations of me until we get to the concrete implementation that
* implements me.
*/publicinterface CompanyDao {void createCompany(Company c);
Company update(Company c);
Company find(Long id);}
To create this interface:
Expand Ejb3Tutorial2 and select the src directory
Right-click, select New::Interface
Enter dao for the package and CompanyDao for the name
Click Finish
Type the code into the file and save it
CompanyDaoImpl.java
packagedao.impl;importjavax.ejb.Stateless;importjavax.ejb.TransactionAttribute;importjavax.ejb.TransactionAttributeType;importjavax.persistence.EntityManager;importjavax.persistence.PersistenceContext;importdao.CompanyDao;importentity.Company;/**
* Note we've used the optional name parameter to give this bean a name in Jndi.
* Since this class only implements one interface and since there are no uses of
* -at- remote here or in the CompanyDao interface, this is a local bean. Thus,
* the name to lookup will be "CompanyDao/local".
*/
@Stateless(name = "CompanyDao")publicclass CompanyDaoImpl implements CompanyDao {/**
* We have defined a persistence unit called custdb. To do so, we must
* update META-INF/persistence.xml. We optionally need to define a new
* data source (for our examples, we're using the default data source
* used by JBoss, which is an in-memory HSQL database instance).
*
* Review comments in persistence.xml regarding the details of defining
* a different data source.
*/
@PersistenceContext(unitName = "custdb")private EntityManager em;/**
* This is now a method running under a transaction manager. Since we've
* defined no transaction attribute, this method's transaction property is
* equal to REQUIRED. If one is not started, then it will be started. So
* when we enter this method, assuming no other transaction has started, the
* container will start a transaction and then when we exit this method, the
* transaction will be committed (recording any changes we've made).
*/publicvoid createCompany(final Company c){
em.persist(c);}public Company update(final Company c){return em.merge(c);}/**
* This find method makes no changes so it can run without a transaction.
*/
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)public Company find(finalLong id){return em.find(Company.class, id);}}
To create this class:
Expand Ejb3Tutorial2 and select the src directory
Right-click, select New::Class
Enter dao.impl for the package and CompanyDaoImpl for the name
Click Finish
Type the code into the file and save it
Starting JBoss EJB 3 Embeddable Container
As with our first tutorial, we need to be able to start the container. Here's the JBossUtil class again. Below is the file again. However, to get this file into your new tutorial:
Expand your first project (Ejb3Tutorial1)
Expand the src directory, select the util package
Copy it (ctrl-c or right-click and select copy)
Expand your second project (Ejb3Tutorial2)
Select the src directory
Paste it (ctrl-v or right-click and select paste)
JBossUtil.java
packageutil;importjava.io.ByteArrayOutputStream;importjava.io.IOException;importjava.io.OutputStream;importjava.io.PrintStream;importjava.util.logging.Logger;importjavax.naming.InitialContext;importjavax.naming.NamingException;importorg.jboss.ejb3.embedded.EJB3StandaloneBootstrap;/**
* This class was originally necessary when using the ALPHA 5 version of the
* embeddable container. With the alpha 9 release, initialization is quite
* simple, you need just 2 lines to initialize your JBoss Embeddable EJB3
* Container Environment. Unfortunately, the one that is available for download
* uses System.out.println() in a few places, so this simple utility hides that
* output and also provides a simple lookup mechanism.
*/publicclass JBossUtil {privatestaticPrintStream originalOut;privatestaticPrintStream originalErr;privatestaticOutputStream testOutputStream;privatestaticPrintStream testOuputPrintStream;staticboolean initialized;staticInitialContext ctx;private JBossUtil(){// I'm a utility class, do not instantiate me}/**
* JBoss EJB3 Embeddable Container uses System.out. Redirect that output to
* keep the console output clean.
*/privatestaticvoid redirectStreams(){// configure the console to get rid of hard-coded System.out's in// the JBoss libraries
testOutputStream = newByteArrayOutputStream(2048);
testOuputPrintStream = newPrintStream(testOutputStream);
originalOut = System.out;
originalErr = System.err;System.setOut(testOuputPrintStream);System.setErr(testOuputPrintStream);}/**
* Restore the System.out and System.err streams to their original state.
* Close the temporary stream created for the purpose of redirecting I/O
* while the initializing is going on.
*/privatestaticvoid restoreStreams(){System.setOut(originalOut);System.setErr(originalErr);
testOuputPrintStream.close();try{
testOutputStream.close();}catch(IOException e){Logger.getLogger(JBossUtil.class.getName()).info("Unable to close testoutstream");}}/**
* This method starts and initializes the embeddable container. We do not
* offer a method to properly clean up the container since this is really
* meant for testing only.
*
* This method may freely be called as often as you'd like since it lazily
* initializes the container only once.
*/publicstaticvoid startDeployer(){if(!initialized){
redirectStreams();
EJB3StandaloneBootstrap.boot(null);
EJB3StandaloneBootstrap.scanClasspath();
initialized = true;
restoreStreams();}}/**
* This is for symmetry. Given how we are using this class, there's little
* need to actually shutdown the container since we run a quick application
* and then stop the JVM.
*/publicstaticvoid shutdownDeployer(){
EJB3StandaloneBootstrap.shutdown();}privatestaticInitialContext getContext(){/**
* We only keep one context around, so lazily initialize it
*/if(ctx == null){try{
ctx = newInitialContext();}catch(NamingException e){thrownewRuntimeException("Unable to get initial context", e);}}return ctx;}/**
* The lookup method on InitialContext returns Object. This simple wrapper
* asks first for the expected type and the for the name to find. It gets
* the name out of JNDI and performs a simple type-check. It then casts to
* the type provided as the first parameter.
*
* This isn't strictly correct since the cast uses the expression (T), where
* T is the generic parameter and the type is erased at run-time. However,
* since we first perform a type check, we know this cast is safe. The -at-
* SuppressWarnings lets the Java Compiler know that we think we know what
* we are doing.
*
* @param <T>
* Type type provided as the first parameter
* @param clazz
* The type to cast to upon return
* @param name
* The name to find in Jndi, e.g. XxxDao/local or, XxxDao/Remote
* @return Something out of Jndi cast to the type provided as the first
* parameter.
*/
@SuppressWarnings("unchecked")publicstatic<T> T lookup(Class<T> clazz, String name){finalInitialContext ctx = getContext();/**
* Perform the lookup, verify that it is type-compatible with clazz and
* cast the return type (using the erased type because that's all we
* have) so the client does not need to perform the cast.
*/try{finalObject object = ctx.lookup(name);if(clazz.isAssignableFrom(object.getClass())){return(T) object;}else{thrownewRuntimeException(String.format("Class found: %s cannot be assigned to type: %s",
object.getClass(), clazz));}}catch(NamingException e){thrownewRuntimeException(String.format("Unable to find ejb for %s", clazz.getName()), e);}}}
A unit test or main program can use the following code to initialize the JBoss EJB 3 Container:
JBossUtil.startDeployer();
Smoke Test Suite
Here is a quick test suite to verify that things basically work. To create this test:
Expand your project (Ejb3Tutorial2)
Select the test source folder
Right-click and select New:Class
Enter dao.impl for the Package
Enter CompanyDaoImplTest for the Class name
Click Finish
Enter the following class
Once the class is saved and compiles, execute it (Right-click in the file, select Run As::JUnit Test
CompanyDaoImplTest.java
packagedao.impl;importstaticorg.junit.Assert.assertEquals;importstaticorg.junit.Assert.assertNotNull;importorg.junit.Before;importorg.junit.BeforeClass;importorg.junit.Test;importutil.JBossUtil;importdao.CompanyDao;importentity.Address;importentity.Company;importentity.Person;publicclass CompanyDaoImplTest {private CompanyDao dao;
@BeforeClass
publicstaticvoid setupJboss()throwsException{
JBossUtil.startDeployer();}
@Before
publicvoid getCompanyDao()throwsException{
dao = JBossUtil.lookup(CompanyDao.class, "CompanyDao/local");}private Company createCompanyImpl(){final Address a = new Address("5080 Spectrum Drive", "Suite 700 West",
"Addison", "TX", "75001");final Company c = new Company("Valtech", a);
dao.createCompany(c);return c;}
@Test
publicvoid createCompany(){final Company c = createCompanyImpl();
assertEquals("Valtech", c.getName());
assertNotNull(c.getId());}
@Test
publicvoid hirePerson(){final Address a = new Address("5080 Spectrum Drive", "Suite 700 West",
"Addison", "TX", "75001");final Person p = new Person("Brett", 'L', "Schuchert", a);final Company c = createCompanyImpl();
c.hire(p);
dao.update(c);final Company found = dao.find(c.getId());
assertEquals(1, found.getEmployees().size());}}
Optional: Data Source Configuration
The persistence.xml file mentions the possibility of using your own data source rather than the default data source to hit a different database.
When you use the embedded container, it looks for a file called embedded-jboss-beans.xml for its datasources (and several other things). In the one that ships with the ALPHA 9 release, you'll see the following two entries near the bottom:
To create your own data source, you basically copy both of these and update the values accordingly. Here is one example that uses HSQL with a local server:
Now that everything is setup, you'll need to open the perspective and form a connection to the database.
Click the Open Perspective button and select Other
Select Quantum DB and click OK
Right-click in Database Bookmarks pane and select New Bookmark...
Click on the Add Driver button
Click on Add External Jar...
Find hsqldb.jar (c:\libs\hsqldb\lib\hsqldb.jar) and click Open
Click on Browse...
Select org.hsqldb.jdbcDriver and click ok
Click on Finish
Select the driver you just added (it is probably the first in the list but look in the JDBC Driver Name column for org.hsqldb.jdbcDriver
Click Next
In the Userid field, enter sa
Leave the Password field blank
Leave Prompt for Password unselected
In the JDBC URL filed, enter jdbc:hsqldb:hsql://localhost/xdb
Name your bookmark HypersonicLocalServer and click Finish
Double-click on you new HypersonicLocalServer Bookmark
Expand PUBLIC
Expand Tables
At this point you can experiment with the plugin.
Exercises
Fetching and Lazy Initialization
The Company entity has one attribute where we've specified the fetch type. Review the employees attribute in Company.java. Remove the fetch = FetchType.EAGER and execute the tests.
There's another way to make this "work" by manually reading the contents of the collection. Experiment with that and see if you can get it to work using this technique. What is the impact of @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) on a lazy fetch? (Hint: you might have some issues with this combination.)
Review
Which approach do you prefer?
What are the advantages/disadvantages?
Give one example where you'd use each technique.
Suggest a way to rewrite this or the test or both to remove the need to perform eager fetching.
Hint: You can compare the SQL from each approach by adding the following line to the persistence.xml:
Retrieve the person (you can add a new DAO or simply add a method to the Company Dao)
Verify that they have one job and that it is for the expected company.
Review
Did this test work? If not, why? If so, what can you say about @OneToMany versus @ManyToOne?
If it did not work, fix it.
Add Tests
Review the driver from the first JPA tutorial. Use that as example source from which you can derive tests.
In addition, add the following tests: Attempt to Hire Person Already Hired
Write a test that creates a Person and 2 companies. Hire the person at the first company. Attempt to hire the person for the second company.
The results should be one of two things (you choose):
The test expects some kind of exception like "Person Has a Job" to be thrown.
You change the relationship between Person and Company to be bi-direction and many to many so that a Person can work for multiple companies.
Hire Person with Same Name/Address
Create two people and hire them both. Make sure this works.
Question, do you think it should work? If not, then update the equals and hashCode method and make this test be one that only works if the attempt fails.
Test Isolation
Use the installed QuantumDB Perspective to discover if our tests are leaving around objects after they have completed. Assuming they have, write code to have each test clean up after itself.
Session Bean and JPA
This tutorial revisits the entities from JPA Tutorial 1 - Getting Started and introduces a DAO, which gets its entity manager auto-wired.
Setting Up Your Project
The instructions for setting up your project mirror those from the first tutorial: EJB3 Tutorial 1 - Create and Configure.For the remainder of this tutorial, when you see <project>, replace it with Ejb3Tutorial2.
Create the Project
Edit your project properties
Now that we have created a user library, we can add that user library to our project:Setup the configuration files
The JBoss Embeddable container looks for several files in the classpath. To keep all of these in one place, we'll add another source directory to our project and then import several files into that directory.Add Resource Adapter Archive(RAR)
The Java Connector system defines Resource Adapter Archive files (RAR files). We need to add a few RAR files into the class path. We will import two more files into the conf directory:Create a jndi.properties file
Note, depending on the version of the embeddable container you download, you might already have a file called jndi.properties. If you do, skip to the next section.Create a persistence.xml
This example presents a utility class we'll be using later. The container needs a persistence.xml file to operate. This file must be found under a META-INF directory somewhere in the classpath or the embeddable container will not start. The file's name is persistence.xml with a lower-case 'p'. On a Unix system, this will make a difference. On a PC, this won't make a difference and it is one of those things that might work on your machine but not on the linux build box.persistence.xml
The Entity Model
Creating the Entity Model
We need to create our entity model. There are three classes in it:To create the basic classes:
Type or enter the source provided below for each of the three classes.
Changes
The entity model is changed slightly from the first JPA tutorial. There is one change in Company.java. Review that class' comments to understand that change. The quick summary is that we've added eager fetching to a relationship.Address.java
Company.java
Person.java
The Dao and its Interface
We will create a session bean that wraps access to the company behind a session bean interface. This is another example of the Data Access Object (dao) pattern. This is one of the typical ways we use a session bean.This dao looks pretty much the same as the session bean from our first EJB example. As with the first example, we've placed the interface in one package and the implementation (the session bean) in a second interface.
CompanyDao.java
To create this interface:
CompanyDaoImpl.java
To create this class:
Starting JBoss EJB 3 Embeddable Container
As with our first tutorial, we need to be able to start the container. Here's the JBossUtil class again. Below is the file again. However, to get this file into your new tutorial:JBossUtil.java
A unit test or main program can use the following code to initialize the JBoss EJB 3 Container:
Smoke Test Suite
Here is a quick test suite to verify that things basically work. To create this test:CompanyDaoImplTest.java
Optional: Data Source Configuration
The persistence.xml file mentions the possibility of using your own data source rather than the default data source to hit a different database.When you use the embedded container, it looks for a file called embedded-jboss-beans.xml for its datasources (and several other things). In the one that ships with the ALPHA 9 release, you'll see the following two entries near the bottom:
To create your own data source, you basically copy both of these and update the values accordingly. Here is one example that uses HSQL with a local server:
To use this, you need to do two things. Update persistence.xml and start a HSQL server.
Update persistence.xml
Replace the following line:
With this one:
To start an HSQL server, you can use the following steps:
Optional: QuantumDb Setup and Configuration
QuantumDb Configuration
QuantumDb is an Eclipse plugin that lets you view a database. This is a quick start guide.
Download and Install
Start your Database
This example assumes hypersonic is running with the following startup script:In our examples, we created a folder called database under the installation directory of hypersonic, so the full folder name is:
Assuming java is in your classpath, the following command will start hypersonic:
JPA in JSE Settings
persistence.xmlJPA in JEE Settings
This assumes you are using the JBoss Embedded container.And the additions to embedded-jboss-bean.xml:
Using the Perspective
Now that everything is setup, you'll need to open the perspective and form a connection to the database.At this point you can experiment with the plugin.
Exercises
Fetching and Lazy Initialization
The Company entity has one attribute where we've specified the fetch type. Review the employees attribute in Company.java. Remove the fetch = FetchType.EAGER and execute the tests.There's another way to make this "work" by manually reading the contents of the collection. Experiment with that and see if you can get it to work using this technique. What is the impact of @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) on a lazy fetch? (Hint: you might have some issues with this combination.)
Review
Hint: You can compare the SQL from each approach by adding the following line to the persistence.xml:
Fetch and Lazy Initialization Revisited
Add a unit test where you:- Create a person
- Create a company
- Hire the person
- Retrieve the person (you can add a new DAO or simply add a method to the Company Dao)
- Verify that they have one job and that it is for the expected company.
ReviewAdd Tests
Review the driver from the first JPA tutorial. Use that as example source from which you can derive tests.In addition, add the following tests:
Attempt to Hire Person Already Hired
Write a test that creates a Person and 2 companies. Hire the person at the first company. Attempt to hire the person for the second company.
The results should be one of two things (you choose):
Hire Person with Same Name/Address
Create two people and hire them both. Make sure this works.
Question, do you think it should work? If not, then update the equals and hashCode method and make this test be one that only works if the attempt fails.
Test Isolation
Use the installed QuantumDB Perspective to discover if our tests are leaving around objects after they have completed. Assuming they have, write code to have each test clean up after itself.